package org.example.acquireClassInfo;

import javassist.*;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
import javassist.expr.NewExpr;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {

    public static String $INSERT_CODE_BEFORE = "System.out.println(\"$_=$proceed($$) 语句执行之前\");";

    public static String $INSERT_CODE_AFTER = "System.out.println(\"$_=$proceed($$) 语句执行之后\");";

    public static void main(String[] args) throws NotFoundException, CannotCompileException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
        ClassPool pool = ClassPool.getDefault();
//        pool.appendClassPath("F:\\javacode\\javassistDemo\\javassistDemo\\src\\main\\java");
        CtClass ctClass = pool.get("org.example.acquireClassInfo.Calculator");

        // 获取一个类中的所有方法
        CtMethod[] declaredMethods = ctClass.getDeclaredMethods();
        System.out.println("======================获取类中所有的方法==========================");
        for(CtMethod method : declaredMethods){
            System.out.println("method.toString(): "+method.toString());
            System.out.println("method.getName(): "+method.getName());
            System.out.println("Modifier.isPublic(method.getModifiers()): "+Modifier.isPublic(method.getModifiers()));
            System.out.println();
            /*
                获取 method中调用别处方法的methodcall m 操作
            * */
            method.instrument(new ExprEditor(){
                  @Override
                  public void edit(MethodCall m) throws CannotCompileException {
                      System.out.println(m.getMethodName()+"\t\t"+m.getClassName()+"\t\t"+m.getLineNumber()+"\t\t"+m.getSignature());
                      if (m.getClassName().equals("org.example.Hello") && m.getMethodName().equals("say")) {
                          m.replace("System.out.println(\"替换 org.example.Hello.say()方法\");");
                      }
                  }
            });
        }
        System.out.println("======================获取方法结束===============================");

        System.out.println("\n\n\n\n");

        // 测试 &_ 和 &&
        System.out.println("=======================测试 &_ 和 &&=========MethodCall=============");
        CtMethod callHelloAdd = ctClass.getDeclaredMethod("callHelloAdd");
        callHelloAdd.instrument(new ExprEditor(){
            @Override
            public void edit(MethodCall m) throws CannotCompileException{
                if(m.getClassName().equals("org.example.Hello") && m.getMethodName().equals("add")){
                    // $_代表这条MethodCall语句的返回值，$proceed代表执行这个MethodCall即执行method2，传入的参数和原来的入参一样
                    m.replace($INSERT_CODE_BEFORE+"\n"+"$_=$proceed($$);\n"+$INSERT_CODE_AFTER); // 在某个调用前后插入代码
                }
            }
        });
        System.out.println("=======================测试 &_ 和 && end========================");

        System.out.println("\n\n\n\n");

        System.out.println("=========================测试NewExper===========================");
        callHelloAdd.instrument(new ExprEditor(){
            @Override
            public void edit(NewExpr m) throws CannotCompileException{
                System.out.println(m.getClassName());
                if(m.getClassName().equals("org.example.Hello")){
                    System.out.println("m.getSignature(): "+m.getSignature());
                    System.out.println("m.getLineNumber(): "+m.getLineNumber());
                    // 在一个new 语句执行前后添加语句
                    m.replace("System.out.println(\"new()语句 语句执行之前\");"+"\n"+"$_=$proceed($$);\n"+"System.out.println(\"new()语句 语句执行之后\");");
                }
            }
        });
        System.out.println("=========================测试NewExper end=======================");
        System.out.println("\n\n\n\n");

        System.out.println("=========================打印修改后的结果=========================");
        // 执行上面instrument代码修改过的代码
        Object calc = ctClass.toClass().newInstance();
        // 调用printPrivateVar 方法
        Method printPrivateVar = calc.getClass().getMethod("printPrivateVar");
        printPrivateVar.invoke(calc);

        // 调用callHelloAdd
        Method callHelloAdd1 = calc.getClass().getMethod("callHelloAdd");
        callHelloAdd1.invoke(calc);
        System.out.println("=========================打印修改后的结果 end=====================");
        System.out.println("\n\n\n\n");

        if(ctClass.isFrozen()){
            ctClass.defrost();
        }


        CtField[] declaredFields = ctClass.getDeclaredFields();
        System.out.println("======================获取类中所有的属性==========================");
        for(CtField ctField : declaredFields){
            System.out.println("ctField.getType(): "+ctField.getType());
            System.out.println("ctField.getName(): "+ctField.getName());
            System.out.println("ctField.getSignature(): "+ctField.getSignature());
            System.out.println("ctField.getFieldInfo(): "+ctField.getFieldInfo());
            System.out.println();
        }
        System.out.println("======================获取属性结束===============================");
        System.out.println("\n\n\n\n");

        System.out.println("ctClass.getPackageName(): "+ctClass.getPackageName());
        System.out.println("ctClass.getModifiers(): "+ctClass.getModifiers());
        System.out.println("ctClass.getName(): "+ctClass.getName());
    }
}
